package com.threefish.common.plugins.shiro;


import com.threefish.common.constant.Cons;
import com.threefish.common.utils.StringUtils;
import com.threefish.modules.sys.dao.MenuDao;
import com.threefish.modules.sys.dao.UserAccountDao;
import com.threefish.modules.sys.entity.organize.UserAccount;
import com.threefish.modules.sys.entity.setting.Menu;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.nutz.lang.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 认证
 *
 * @author 黄川 huchuc@vip.qq.com
 * @date: 2018/9/7
 */
@Component
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserAccountDao sysUserDao;

    @Autowired
    private MenuDao sysMenuDao;

    /**
     * 授权(验证权限时调用)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession(false);
        SimpleAuthorizationInfo auth = (SimpleAuthorizationInfo) session.getAttribute(Cons.SHIRO_AUTHORIZATION_INFO);
        if (!subject.isAuthenticated() || auth == null) {
            UserAccount user = (UserAccount) session.getAttribute(Cons.SESSION_USER_KEY);
            if (user != null) {
                auth = new SimpleAuthorizationInfo();
                auth.addRole(Cons.SESSION_USER_ROLE);
                String userId = user.getUserId();
                List<String> permsList;
                //系统管理员，拥有全部权限
                if (Cons.SUPER_ADMIN.equals(user.getUserName())) {
                    auth.addRole(Cons.SUPER_ADMIN);
                    List<Menu> menuList = sysMenuDao.query();
                    permsList = new ArrayList<>(menuList.size());
                    for (Menu menu : menuList) {
                        permsList.add(menu.getPermission());
                    }
                } else {
                    permsList = sysUserDao.queryAllPerms(userId);
                }
                //用户权限列表
                Set<String> permsSet = new HashSet<>();
                for (String perms : permsList) {
                    if (StringUtils.isBlank(perms)) {
                        continue;
                    }
                    permsSet.addAll(Arrays.asList(perms.trim().split(",")));
                }
                auth.setStringPermissions(permsSet);
                session.setAttribute(Cons.SHIRO_AUTHORIZATION_INFO, auth);
            }
        }
        return auth;
    }

    /**
     * 认证(登录时调用)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        ShiroUserToken token = (ShiroUserToken) authcToken;
        String password = String.valueOf(token.getPassword());
        if (Strings.isBlank(token.getUsername())) {
            throw new AccountException("账户不能为空！");
        } else if (token.getLoginType() == null) {
            throw new AccountException("请设置登录类型！");
        }
        boolean needVerifyPass = token.needVerifyPass();
        if (needVerifyPass) {
            if (Strings.isBlank(password)) {
                throw new AccountException("密码不能为空！");
            }
        }
        UserAccount userAccount = sysUserDao.loginFind(token.getUsername());
        sysUserDao.fetchLinks(userAccount, null);
        if (userAccount == null) {
            throw new AccountException("账号或密码无效！");
        }
        if (userAccount.isLocked()) {
            throw new LockedAccountException("登录失败！帐号已被冻结！");
        }
        if (needVerifyPass) {
            //效验密码规则
            Sha256Hash sha = new Sha256Hash(password, userAccount.getSalt());
            if (!sha.toHex().equals(userAccount.getUserPass())) {
                throw new AccountException("账号或密码无效！");
            }
        }
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession(false);
        session = (session == null ? subject.getSession(true) : session);
        session.setAttribute(Cons.SESSION_USER_KEY, userAccount);
        token.setPassword(userAccount.getUserPass().toCharArray());
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(token, password, getClass().getName());
        simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(userAccount.getSalt()));
        return simpleAuthenticationInfo;
    }


    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        super.setCredentialsMatcher(new Sha256HashCredentialsMatcher());
    }
}
